﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace ChatServer
{
    public class StatusChangedEventArgs : EventArgs
    {
        //event argument, dit zal het bericht worden wat de gebruiker te zien krijgt
        private string eventMsg;

        //zet in server.cs de message in de event handler
        public string eventMessage
        {
            get
            {
                return eventMsg;
            }
            set
            {
                eventMsg = value;
            }
        }

        //set event message
        public StatusChangedEventArgs(string strEventMessage)
        {
            eventMsg = strEventMessage;
        }
    }

    //hier komen de parameters voor de events in
    public delegate void StatusChangedEventHandler(Object sender, StatusChangedEventArgs e);

    class Monitor
    {
        //hashtable waar gebruikers en connecties in komen te zitten. max 30
        public static Hashtable UserTable = new Hashtable(30);
        public static Hashtable ConnectionTable = new Hashtable(30);

        //variabelen voor server
        private IPAddress ip;
        private int port;
        private TcpClient server;

        //event die later gebruikt wordt
        public static event StatusChangedEventHandler StatusChangedEvent;
        private static StatusChangedEventArgs e;

        //luistert naar binnenkomende connecties
        private TcpListener tcpListener;

        //thread die naar binnenkomende connecties luistert
        private Thread threadListener;

        //als de boolean true is zal de server starten
        Boolean running = false;


        //vangt doorgestuurde data af
        public Monitor(IPAddress ipAddress, int serverPort)
        {
            ip = ipAddress;
            port = serverPort;
        }

        public void StartMonitoring()
        {
            //haal ip adres binnen
            IPAddress localIP = ip;
            int localPort = port;

            //start de listener en begin met het controleren of personen connecten
            tcpListener = new TcpListener(localIP, localPort);
            try
            {
                tcpListener.Start();
                
                running = true;

                //start de thread die de host laat listenen
                threadListener = new Thread(Listening);
                threadListener.Start();
            }
            catch(SocketException)
            {
                StopListening();
            }
        }

        private void Listening()
        {
            //terwijl de server runt
            while (running == true)
            {
                //accepteer connectie
                server = tcpListener.AcceptTcpClient();
                //maak nieuwe connectie voor de gebruiker aan
                Connection newConnection = new Connection(server);
            }
        }

        public void StopListening()
        {
            //disconnect alles en stuur een bericht dat de server gedisconnect is
            SendAdminMessage("De verbinding naar de server is verbroken, probeer het opnieuw");
            UserTable.Clear();
            ConnectionTable.Clear();
            ip = null;
            port = 0;
            
            tcpListener.Stop();

            running = false;
            
            //deze variabelen kunnen null zijn als niemand aan de server heeft geconnect
            if (threadListener != null)
            {
                threadListener.Abort();
            }
            if (server != null)
            {
                server.Close();
            } 
        }

        public static void AddUser(TcpClient tcpUser, string Username)
        {
            //add gegevens in hashtables
            Monitor.UserTable.Add(Username, tcpUser);
            Monitor.ConnectionTable.Add(tcpUser, Username);

            //vertelt iedereen dat er iemand connected is
            SendAdminMessage(ConnectionTable[tcpUser]+ " has joined the server");
        }

        
        public static void RemoveUser(TcpClient tcpUser)
        {
            try
            {
                //controleren of de gebruiker nog in de tabel zit
                if (Monitor.ConnectionTable[tcpUser] != null) //--> i modified this part
                {
                    string users = Monitor.ConnectionTable[tcpUser].ToString();

                    // EERST de user verwijderen, daarna message sturen, dit zorgt ervoor dat hij niet gaat spammen zoals eerst
                    Monitor.UserTable.Remove(Monitor.ConnectionTable[tcpUser]);
                    Monitor.ConnectionTable.Remove(tcpUser);

                    SendAdminMessage(users+" has left us");
                    users = null;
                }
            }
            catch (InvalidOperationException e)
            {
                Console.WriteLine(e.Message);
            }
        }
        
        public static void SendAdminMessage(string Message)
        {
            StreamWriter adminMessageSender;

            //laten zien wie wat zegt
            e = new StatusChangedEventArgs("Administrator: "+Message);
            OnStatusChanged(e);

            //array van tcp clients maken (zelfde als aantal connecties)
            TcpClient[] tcpclients = new TcpClient[Monitor.UserTable.Count];
            //tcp clients in array stoppen
            Monitor.UserTable.Values.CopyTo(tcpclients, 0);

            //loopen door de lijst zodat iedereen een bericht krijgt
            for (int i = 0; i < tcpclients.Length; i++)
            {
                try
                {
                    if (Message.Trim() == "" || tcpclients[i] == null)
                    {
                        continue;
                    }

                    adminMessageSender = new StreamWriter(tcpclients[i].GetStream());
                    adminMessageSender.WriteLine("Administrator: "+Message);
                    adminMessageSender.Flush();
                    adminMessageSender = null;

                    //userlist toesturen                  
                    sendUserlist(tcpclients[i]);
                }
                catch //als een gebruiker niet meer actief is verwijderen
                {
                    RemoveUser(tcpclients[i]);
                }
            }
        }

        public static void sendUserlist(TcpClient x)
        {
            StreamWriter userlistUpdate;

            //array met users die doorgestuurd word naar de client
            string[] Userlist = new string[Monitor.UserTable.Count];

            //hashtable met users converteren naar array, keys = usernames
            UserTable.Keys.CopyTo(Userlist, 0);

            //userlist sturen, moet door elke user in de array loopen
            for (int i = 0; i <= Userlist.Length; i++)
            {
                if (i == Userlist.Length)
                {
                    //message dat er geen users meer gedetecteerd zijn en dat de loop kan stoppen
                    userlistUpdate = new StreamWriter(x.GetStream());
                    userlistUpdate.WriteLine("4|");
                    userlistUpdate.Flush();
                    userlistUpdate = null;
                    break;
                }
                else
                {
                    userlistUpdate = new StreamWriter(x.GetStream());
                    userlistUpdate.WriteLine("3|" + Userlist[i]);
                    userlistUpdate.Flush();
                    userlistUpdate = null;
                }
            }
        }

        public static void OnStatusChanged(StatusChangedEventArgs e)
        {
            StatusChangedEventHandler statusHandler = StatusChangedEvent;

            if (statusHandler != null)
            {
                statusHandler(null, e);
            }
        }

        public static void SendMessage(string From, string Message)
        {
            StreamWriter Sender;

            //laten zien wie wat zegt
            e = new StatusChangedEventArgs(From + " zegt: " + Message);
            OnStatusChanged(e);

            //array aanmaken van tcp clients, moet gelijk zijn aan het aantal users wat connected is
            TcpClient[] tcpClients = new TcpClient[Monitor.UserTable.Count];

            //tcpclient objecten in array zetten
            Monitor.UserTable.Values.CopyTo(tcpClients, 0);

            //loopen door tcp clients
            for (int i = 0; i < tcpClients.Length; i++)
            {
                //message sturen naar alle clients
                try
                {
                    if (Message.Trim() == "" || tcpClients[i] == null)
                    {
                        continue;
                    }
                    
                    //message sturen naar persoon in de loop
                    Sender = new StreamWriter(tcpClients[i].GetStream());
                    Sender.WriteLine(From + " zegt: " + Message);
                    Sender.Flush();
                    Sender = null;
                }
                catch
                {
                    RemoveUser(tcpClients[i]);
                }
            }
        }
    }
}